//
//  KLClient.m
//  WebRTCDemo_iOS
//
//  Created by szblsx2 on 2020/3/5.
//  Copyright © 2020 szblsx2. All rights reserved.
//

#import "KLClient.h"
#import "KLWebSocket.h"
#import "KLTool.h"

@interface KLClient ()
//KLWebSocket对象
@property (nonatomic, strong) KLWebSocket *mKLWebSocket;
//delegate
@property (nonatomic, weak) id<KLClientDelegate> delegate;
//我的用户id
@property (nonatomic, copy) NSString *myUserId;
//首次进入房间时的主持人id
@property (nonatomic, copy) NSString *firstAdminId;
//加入房间回调
@property (nonatomic, copy) void(^joinRoomBlock)(BOOL result);

@end

@implementation KLClient
{
    NSInteger _publishId;     //临时变量(发送推流时的请求id)
    NSInteger _screenId;      //临时变量(发送共享屏幕时的请求id)
    NSInteger _joinRoomId;    //临时变量(发送加入房间时的请求id)
    NSString *_currentRoomId; //临时变量(当前的房间id)
    NSInteger _userListId;    //临时变量(发送房间用户列表的请求id)
    NSInteger _leaveRoomId;   //临时变量(发送离开房间时的请求id)
    
}


- (instancetype)initWithDelegate:(id<KLClientDelegate>)delegate
{
    if (self = [super init]) {
        _delegate = delegate;
    }
    return self;
}

/**连接scoket*/
- (void)startWithServerUrl:(NSString *)sevrerUrl success:(void(^)(BOOL))success
{
    //先关闭
    [self.mKLWebSocket closeWebSocket];
    //再连接
    [self.mKLWebSocket connectWebSocketWithUrlString:sevrerUrl];
    //处理事件
    [self handleDataRecvice];
    //事件回调
    [self eventHandleComplete:^(BOOL status) {
        if (success) {
            success(status);
        }
    }];
}

/**断开连接*/
- (void)close
{
    [self.mKLWebSocket closeWebSocket];
}

#pragma mark - 发送信令

/**加入房间*/
- (void)sendJoin:(NSString *)rid uid:(NSString *)uid info:(NSDictionary *)info completion:(void(^)(BOOL result))completion
{
    _joinRoomId = [KLTool toRandomId];
    _currentRoomId = rid;
    _myUserId = uid;
    _joinRoomBlock = completion;
    NSDictionary *dic = @{ @"request": @(true),
                           @"id":@(_joinRoomId),
                           @"method":@"join",
                           @"data":@{@"rid":rid,
                                     @"uid":uid,
                                     @"info":info}
    };
    NSLog(@"加入房间: %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**离开房间*/
- (void)sendLeave:(NSString *)rid uid:(NSString *)uid
{
    _leaveRoomId = [KLTool toRandomId];
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@(_leaveRoomId),
                          @"method":@"leave",
                          @"data":@{@"rid":rid,@"uid":uid }
    };
    NSLog(@"离开房间 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**发布流*/
- (void)sendPublish:(NSString *)rid sdp:(NSString *)sdp bScreen:(BOOL)bScreen
{
    NSInteger publishId;
    if (bScreen) {
        _screenId = [KLTool toRandomId];
        publishId = _screenId;
    }else{
        _publishId = [KLTool toRandomId];
        publishId = _publishId;
    }
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@(publishId),
                          @"method":@"publish",
                          @"data":@{@"rid":rid,@"jsep":@{@"sdp":sdp,@"type":@"offer"},@"options":@{@"codec":@"h264",@"resolution":@"hd",@"bandwidth":@1024,@"audio":@(true),@"video":@(true),@"screen":@(bScreen)}}};
    
    NSLog(@"发布流 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**取消发布流*/
- (void)sendUnpublish:(NSString *)rid mid:(NSString *)mid
{
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@([KLTool toRandomId]),
                          @"method":@"unpublish",
                          @"data":@{@"rid":rid,@"mid":mid}};
    NSLog(@"取消发布流 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**订阅流*/
- (void)sendSubscribe:(NSString *)rid mid:(NSString *)mid sdp:(NSString *)sdp
{
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@([KLTool toRandomId]),
                          @"method":@"subscribe",
                          @"data":@{@"rid":rid,@"jsep":@{@"sdp":sdp,@"type":@"offer"},@"mid":mid}};
    NSLog(@"订阅流 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**取消订阅流*/
- (void)sendUnsubscribe:(NSString *)rid mid:(NSString *)mid
{
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@([KLTool toRandomId]),
                          @"method":@"unsubscribe",
                          @"data":@{@"rid":rid,@"mid":mid}};
    NSLog(@"取消订阅流 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**发送IceCandidate*/
- (void)sendIceCandidate:(NSString *)rid mid:(NSString *)mid object:(NSDictionary *)object bPub:(BOOL)bPub
{
    NSDictionary *dic = @{  @"request":@(true),
                            @"id":@([KLTool toRandomId]),
                            @"method":@"trickle",
                            @"data":@{@"uid": @"uid",@"info": @"info",@"rid":rid,@"mid":mid,@"trickle":object,@"isPub":@(bPub)}};
    if (bPub) {
        NSLog(@"推流发送IceCandidate : %@",[KLTool dictToJson:dic]);
    }else{
        NSLog(@"订阅发送IceCandidate : %@",[KLTool dictToJson:dic]);
    }
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**查询房间用户列表*/
- (void)sendRoomUserListData
{
    _userListId = [KLTool toRandomId];
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@(_userListId),
                          @"method":@"listusers",
                          @"data":@{@"rid":_currentRoomId ?: @""}};
    NSLog(@"请求用户列表 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

/**发送会议广播*/
- (void)sendMeetingNotification:(NSString *)rid type:(KLNotifiType)notifiType myUserId:(NSString *)myUserId targetUserId:(nonnull NSString *)targetUserId state:(BOOL)state
{
    NSDictionary *userInfo;
    if (notifiType == 0) {
        userInfo = @{@"type":@(notifiType),@"state" : @(state)};
    }else{
        userInfo = @{@"type":@(notifiType),@"state" : @(state),@"targetUserId":targetUserId ?: @""};
    }
    NSDictionary *dic = @{@"request":@(true),
                          @"id":@([KLTool toRandomId]),
                          @"method":@"broadcast",
                          @"data":@{@"rid":rid,@"uid":myUserId,@"info":userInfo}};
    
    // notifiType 0.全员静音 1.单人静音、开关麦克风 2.开关摄像头 3.主持人变更 4.会议踢人
    NSLog(@"发送会议广播 : %@",[KLTool dictToJson:dic]);
    [_mKLWebSocket sendData:[KLTool dictToJson:dic]];
}

#pragma mark - 接收信令

/**接收服务端信息*/
- (void)handleDataRecvice
{
    __weak __typeof(self) ws = self;
    self.mKLWebSocket.onDataRecvice = ^(NSString * _Nonnull message) {
        NSLog(@"收到服务端信息:%@",message);
        NSDictionary *result = [KLTool jsonToDict:message];
        if ([result.allKeys containsObject:@"response"]) {
            BOOL bResp = [result[@"response"] boolValue];
            if (bResp) {
                // 根据请求id 解析对应的返回处理
                NSInteger responseId = [result[@"id"] integerValue];
                NSString *type = result[@"data"][@"jsep"][@"type"];
                if ([@"answer" isEqualToString:type]) {
                    NSDictionary *jsonObjDict = result[@"data"];
                    NSString *sdp = jsonObjDict[@"jsep"][@"sdp"] ?: @"";
                    NSString *mid = jsonObjDict[@"mid"] ?: @"";
                    if (responseId == self->_publishId) {
                        if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respPublishWithMid:answerSdp:)]) {
                            [ws.delegate klClient:ws respPublishWithMid:mid answerSdp:sdp];
                        }
                    }else if (responseId == self->_screenId){
                        if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respScreenWithMid:answerSdp:)]) {
                            [ws.delegate klClient:ws respScreenWithMid:mid answerSdp:sdp];
                        }
                    }else{
                        NSString *sid = jsonObjDict[@"sid"] ?: @"";
                        if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respSubscribeWithMid:subId:answerSdp:)]) {
                            [ws.delegate klClient:ws respSubscribeWithMid:mid subId:sid answerSdp:sdp];
                        }
                    }
                }
                
                //加入房间成功响应
                if (responseId == self->_joinRoomId) {
                    BOOL joinResult = [result[@"ok"] boolValue];
                    if (ws.joinRoomBlock) {
                        ws.joinRoomBlock(joinResult);
                    }
                    if (joinResult) {
                        NSDictionary *jsonObjDict = result[@"data"];
                        ws.firstAdminId = jsonObjDict[@"admin"] ?: @"";
                        //查询房间用户
                        [ws sendRoomUserListData];
                    }
                }
                
                //查询房间用户列表响应
                if (responseId == self->_userListId) {
                    NSArray *userList = result[@"data"][@"users"] ?: @[];
                    NSMutableArray *meetUserList = [NSMutableArray arrayWithCapacity:userList.count];
                    [userList enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                        NSDictionary *userDict = obj;
                        KLMeetUser *user = [KLMeetUser new];
                        user.userId = userDict[@"uid"] ?: @"";
                        user.streamIds = userDict[@"mids"] ?: @[];
                        user.userName = userDict[@"info"][@"name"];
                        user.userHead = userDict[@"info"][@"head"];
                        if ([user.userId isEqualToString:ws.myUserId]) {
                            user.userKind = kMeetUserLocal;
                            [meetUserList insertObject:user atIndex:0];
                        }else{
                            user.userKind = kMeetUserRemote;
                            [meetUserList addObject:user];
                        }
                    }];
                    if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respRoomUserList:)]) {
                        [ws.delegate klClient:ws respRoomUserList:meetUserList];
                    }
                }
                
                //离开房间成功响应
                if (responseId == self->_leaveRoomId) {
                    BOOL leaveResult = [result[@"ok"] boolValue];
                    if (leaveResult) {
                        //断开socket
                        [ws close];
                    }
                }
            }
        }
        
        if ([result.allKeys containsObject:@"notification"]){
            BOOL bResp = [result[@"notification"] boolValue];
            if (bResp && [result.allKeys containsObject:@"method"]) {
                NSString *method = result[@"method"];
                if ([@"peer-join" isEqualToString:method]) {
                    KLMeetUser *newUser = [[KLMeetUser alloc] init];
                    newUser.userKind = kMeetUserRemote;
                    newUser.userId = result[@"data"][@"uid"];
                    newUser.userName = result[@"data"][@"info"][@"name"];
                    newUser.userHead = result[@"data"][@"info"][@"head"];
                    if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respPeerJoinWithNewUser:)]) {
                        [ws.delegate klClient:ws respPeerJoinWithNewUser:newUser];
                    }
                }
                else if ([@"peer-leave" isEqualToString:method]) {
                    NSString *userId = result[@"data"][@"uid"] ?: @"";
                    if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respPeerLeaveWithUserId:)]) {
                        [ws.delegate klClient:ws respPeerLeaveWithUserId:userId];
                    }
                }
                else if ([@"stream-add" isEqualToString:method]) {
                    NSString *mid = result[@"data"][@"mid"] ?: @"";
                    NSString *userId = result[@"data"][@"uid"] ?: @"";
                    if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respStreamAddWithStreamId:userId:)]) {
                        [ws.delegate klClient:ws respStreamAddWithStreamId:mid userId:userId];
                    }
                }
                else if ([@"stream-remove" isEqualToString:method]) {
                    if ([result.allKeys containsObject:@"data"]) {
                        NSDictionary *jsonObjDict = result[@"data"];
                        NSString *mid = jsonObjDict[@"mid"] ?: @"";
                        NSString *userId = jsonObjDict[@"uid"] ?: @"";
                        if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respStreamRemoveWithMid:userId:)]) {
                            [ws.delegate klClient:ws respStreamRemoveWithMid:mid userId:userId];
                        }
                    }
                }
                else if ([@"broadcast" isEqualToString:method]) {
                    NSDictionary *jsonObjDict = result[@"data"];
                    NSDictionary *notifiInfo = jsonObjDict[@"info"];
                    if (notifiInfo.allKeys.count > 0)
                    {
                        KLNotifiType type = [notifiInfo[@"type"] integerValue];
                        NSString *targetUserId = notifiInfo[@"targetUserId"] ?: @"";
                        BOOL state = [notifiInfo[@"state"] boolValue];
                        
                        if (ws.delegate && [ws.delegate respondsToSelector:@selector(klClient:respMeetingNotificationWithType:targetUserId:state:)]) {
                            [ws.delegate klClient:ws respMeetingNotificationWithType:type targetUserId:targetUserId state:state];
                        }
                    }
                }
            }
        }
    };
}

/**状态回调*/
- (void)eventHandleComplete:(void(^)(BOOL))complete
{
    __weak __typeof(self) ws = self;
    _mKLWebSocket.socketConnect = ^(BOOL success) {
        if (ws.socketStatusBlock) {
            ws.socketStatusBlock(success ? 200 : 201);
        }
        complete(success);
    };
}

#pragma mark - Getter

- (KLWebSocket *)mKLWebSocket
{
    if (!_mKLWebSocket) {
        _mKLWebSocket = [[KLWebSocket alloc] init];
    }
    return _mKLWebSocket;
}

@end
